package main

import (
	"JudgeSandBox/cgroup"
	"JudgeSandBox/common"
	"JudgeSandBox/utils"
	"bytes"
	"context"
	"encoding/json"
	"os"
	"os/exec"
	"path"
	"time"
)

/*
一般
*/
func general(request common.JudgeRequest, response common.JudgeResponse, args []string) {
	mntPath := path.Join(common.TmpPath, request.Uuid, "mnt")
	//剩余时间 毫秒
	timeLeft := request.MaxRunningTimeLimit

	for index, useCase := range request.UseCases {
		//超时限制
		ctx, cancelFunc := context.WithTimeout(context.Background(), time.Duration(timeLeft)*time.Millisecond)

		// chroot /root/judge/tmp/xxx/mnt /java/jdk-11/bin/java Main
		cmd := exec.CommandContext(ctx, "chroot", append([]string{mntPath}, args...)...)
		//cmd := exec.CommandContext(ctx, args[0], args[1:]...) //采用pivot_root
		cmd.Stdin = bytes.NewBufferString(useCase) //输入测试用例

		startTime := time.Now()
		output, err := cmd.CombinedOutput()
		elapsedTime := time.Since(startTime) / time.Millisecond // duration in ms
		cancelFunc()
		utils.PrintToLog("call 执行输出：", string(output))
		utils.PrintToLog("call 执行时间：", int(elapsedTime), "ms")
		if err != nil {
			utils.PrintToLog("call 执行chroot出错", err)
			//先检查是否是因为oom
			if oomCheck, oomErr := cgroup.OOMCheck(request.Uuid); oomErr == nil {
				if oomCheck {
					response.ResultType = common.MemoryLimitExceeded
					response.ErrorMessage = "超出内存限制"
					response.AnswerErrorIndex = index
					break
				}
			} else {
				utils.PrintToLog("call oom", oomErr)
			}

			//再检查是否超时
			if int(elapsedTime) > timeLeft {
				response.ResultType = common.TimeLimitExceeded
				response.ErrorMessage = "超出时间限制"
				response.AnswerErrorIndex = index
				break
			}

			//其他情况都算运行时错误，如数组越界等
			response.ResultType = common.RuntimeError
			response.ErrorMessage = string(output)
			response.AnswerErrorIndex = index
			break
		} else {
			//运行时未出现错误也可能答案不正确
			ans := trim(string(output))
			if ans != request.Answers[index] {
				response.ResultType = common.WrongAnswer
				response.ErrorMessage = ans
				response.AnswerErrorIndex = index
				//将结果写入results
				//strings := append(response.Results, trim(string(output)))
				//response.Results = strings
				response.Results[index] = trim(string(output))
				break
			}
		}

		//减少剩余时间
		timeLeft -= int(elapsedTime)
		//将结果写入results
		//strings := append(response.Results, trim(string(output)))
		//response.Results = strings
		response.Results[index] = trim(string(output))
	}

	if response.ResultType == common.MemoryLimitExceeded ||
		response.ResultType == common.TimeLimitExceeded ||
		response.ResultType == common.RuntimeError ||
		response.ResultType == common.WrongAnswer {

	} else {
		//所有答案正确
		response.ResultType = common.Accepted
		maxUsedMemory, _ := cgroup.GetMaxMemory(response.Uuid)
		response.UsedMaxMemory = int(maxUsedMemory)                   //直接返回所用字节
		response.RunningTime = request.MaxRunningTimeLimit - timeLeft //剩余运行时间
		response.AnswerErrorIndex = len(request.Answers) - 1
	}

	//通过管道将数据写回父进程
	marshal, err := json.Marshal(response)
	if err != nil {
		utils.PrintToLog("call", "结果生成出错", err)
	}
	if write, err := os.Stdout.WriteString(string(marshal)); err != nil {
		utils.PrintToLog("call 写回父进程出错：", write, err)
	}
	if err = os.Stdout.Close(); err != nil {
		utils.PrintToLog("call 关闭stdout出错：", err)
	}
}

/*
acm赛制
*/
func acm(request common.JudgeRequest, response common.JudgeResponse, args []string) {

}

/*
oi赛制
*/
func oi(request common.JudgeRequest, response common.JudgeResponse, args []string) {

}

func Call(args []string) {
	requestBuf := make([]byte, 1024*1024)
	readLen, _ := os.Stdin.Read(requestBuf) //从管道接收评判请求
	utils.PrintToLog("call进程获取评判请求长度：", readLen)
	request := common.JudgeRequest{}
	if unmarshalErr := json.Unmarshal(requestBuf[:readLen], &request); unmarshalErr != nil {
		utils.PrintToLog("call", "评判请求解析错误", unmarshalErr)
	} else {
		utils.PrintToLog("call", "评判请求解析成功", request)
	}

	//结果
	response := common.JudgeResponse{
		Uuid:     request.Uuid,
		Lang:     request.Lang,
		UseCases: request.UseCases,
		Answers:  request.Answers,
		Results:  make([]string, len(request.UseCases)),
	}
	/*mntPath := path.Join(common.TmpPath, request.Uuid, "mnt")
	if err := judge.PivotRoot(mntPath); err != nil {
		utils.PrintToLog(err)
	}*/
	general(request, response, args)
}

//去掉前后空格与回车
func trim(str string) string {
	l := 0
	r := len(str)
	for l < len(str) {
		if str[l] == ' ' || str[l] == '\n' {
			l++
		} else {
			break
		}
	}
	for r > 0 {
		if str[r-1] == ' ' || str[r-1] == '\n' {
			r--
		} else {
			break
		}
	}
	return str[l:r]
}
